package main

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -lm
#include <stdlib.h>
#include <string.h>

typedef struct {
    char** recipients;
    int recipient_count;
    char* subject;
    char* message_text;
    char* message_file;
    char** attachments;
    int attachment_count;
    char* error;

    char* sender_email;
    char* sender_key;
    char* smtp_server;
    int smtp_port;
} EmailState;

static EmailState* createEmailState() {
    EmailState* state = (EmailState*)malloc(sizeof(EmailState));
    if (!state) return NULL;

    state->recipients = (char**)malloc(0);
    state->recipient_count = 0;
    state->subject = NULL;
    state->message_text = NULL;
    state->message_file = NULL;
    state->attachments = (char**)malloc(0);
    state->attachment_count = 0;
    state->error = NULL;

    state->sender_email = NULL;
    state->sender_key = NULL;
    state->smtp_server = NULL;
    state->smtp_port = 0;

    return state;
}

static char** reallocCharArray(char** arr, int newCount) {
    return (char**)realloc(arr, newCount * sizeof(char*));
}

static char* getCharArrayElement(char** arr, int index) {
    return arr[index];
}

static void setCharArrayElement(char** arr, int index, char* value) {
    arr[index] = value;
}
*/
import "C"
import (
	"crypto/tls"
	"fmt"
	"net/smtp"
	"os"
	"strings"
	"unsafe"
)

// --------------------------
// Debug Helper: Print to terminal (visible when running app)
// --------------------------
func debugPrint(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, "[DEBUG] "+format+"\n", args...)
}

//export SendEmailCreateState
func SendEmailCreateState() *C.EmailState {
	debugPrint("Creating new EmailState")
	return C.createEmailState()
}

//export SendEmailAddRecipient
func SendEmailAddRecipient(state *C.EmailState, recipient *C.char) {
	if state == nil || recipient == nil {
		debugPrint("AddRecipient: nil state or recipient")
		return
	}
	recpStr := C.GoString(recipient)
	debugPrint("Adding recipient: %s", recpStr)

	newCount := int(state.recipient_count) + 1
	state.recipients = C.reallocCharArray(state.recipients, C.int(newCount))
	if state.recipients == nil {
		debugPrint("AddRecipient: failed to reallocate array")
		return
	}

	duplicatedRecipient := C.strdup(recipient)
	C.setCharArrayElement(state.recipients, C.int(newCount-1), duplicatedRecipient)
	state.recipient_count = C.int(newCount)
	debugPrint("Recipient added. Total: %d", newCount)
}

//export SendEmailSetSubject
func SendEmailSetSubject(state *C.EmailState, subject *C.char) {
	if state == nil {
		debugPrint("SetSubject: nil state")
		return
	}
	subjStr := C.GoString(subject)
	debugPrint("Setting subject: %s", subjStr)

	if state.subject != nil {
		C.free(unsafe.Pointer(state.subject))
	}
	state.subject = C.strdup(subject)
}

//export SendEmailSetMessageText
func SendEmailSetMessageText(state *C.EmailState, text *C.char) {
	if state == nil {
		debugPrint("SetMessageText: nil state")
		return
	}
	msgStr := C.GoString(text)
	debugPrint("Setting message text (first 100 chars): %s", msgStr[:min(len(msgStr), 100)])

	if state.message_text != nil {
		C.free(unsafe.Pointer(state.message_text))
	}
	state.message_text = C.strdup(text)
}

//export SendEmailAddAttachment
func SendEmailAddAttachment(state *C.EmailState, filePath *C.char) *C.char {
	if state == nil || filePath == nil {
		debugPrint("AddAttachment: nil state or file path")
		return C.CString("invalid input")
	}
	pathStr := C.GoString(filePath)
	debugPrint("Adding attachment: %s", pathStr)

	if _, err := os.Stat(pathStr); os.IsNotExist(err) {
		debugPrint("AddAttachment: file not found: %s", pathStr)
		return C.CString(fmt.Sprintf("file not found: %s", pathStr))
	}

	newCount := int(state.attachment_count) + 1
	state.attachments = C.reallocCharArray(state.attachments, C.int(newCount))
	if state.attachments == nil {
		debugPrint("AddAttachment: out of memory")
		return C.CString("out of memory")
	}

	duplicatedPath := C.strdup(filePath)
	C.setCharArrayElement(state.attachments, C.int(newCount-1), duplicatedPath)
	state.attachment_count = C.int(newCount)
	debugPrint("Attachment added. Total: %d", newCount)
	return nil
}

//export SendEmailSend
func SendEmailSend(state *C.EmailState) *C.char {
	// --------------------------
	// Debug 1: Validate SMTP config (critical!)
	// --------------------------
	if state == nil {
		debugPrint("Send: nil EmailState")
		return C.CString("nil state")
	}
	debugPrint("=== Starting email send ===")
	debugPrint("SMTP Config Check:")
	debugPrint("  Sender Email: %s (nil? %v)", C.GoString(state.sender_email), state.sender_email == nil)
	debugPrint("  Sender Key: %s (nil? %v)", C.GoString(state.sender_key), state.sender_key == nil)
	debugPrint("  SMTP Server: %s (nil? %v)", C.GoString(state.smtp_server), state.smtp_server == nil)
	debugPrint("  SMTP Port: %d", state.smtp_port)

	if state.sender_email == nil || state.sender_key == nil || state.smtp_server == nil || state.smtp_port == 0 {
		debugPrint("Send: SMTP config missing!")
		return C.CString("SMTP config missing")
	}

	// --------------------------
	// Debug 2: Validate recipients
	// --------------------------
	if state.recipient_count == 0 {
		debugPrint("Send: no recipients")
		return C.CString("no recipients")
	}
	recipientCount := int(state.recipient_count)
	recipients := make([]string, recipientCount)
	for i := 0; i < recipientCount; i++ {
		cRecipient := C.getCharArrayElement(state.recipients, C.int(i))
		recipients[i] = C.GoString(cRecipient)
	}
	debugPrint("Recipients (%d): %v", recipientCount, recipients)

	// --------------------------
	// Debug 3: Validate email content
	// --------------------------
	if state.subject == nil || state.message_text == nil {
		debugPrint("Send: missing subject or message")
		return C.CString("missing subject/message")
	}
	subject := C.GoString(state.subject)
	messageText := C.GoString(state.message_text)
	debugPrint("Subject: %s", subject)
	debugPrint("Message Text (first 200 chars): %s", messageText[:min(len(messageText), 200)])

	// --------------------------
	// Debug 4: Build email content (log full RFC 822 format)
	// --------------------------
	emailContent := fmt.Sprintf(
		"To: %s\r\nSubject: %s\r\nContent-Type: text/plain; charset=utf-8\r\nFrom: %s\r\n\r\n%s",
		strings.Join(recipients, ", "),
		subject,
		C.GoString(state.sender_email), // Add From header (critical for deliverability)
		messageText,
	)
	debugPrint("Full Email Content:\n%s", emailContent)

	// --------------------------
	// Debug 5: SMTP Connection (track TLS handshake)
	// --------------------------
	senderEmail := C.GoString(state.sender_email)
	senderKey := C.GoString(state.sender_key)
	smtpServer := C.GoString(state.smtp_server)
	smtpPort := int(state.smtp_port)
	smtpAddr := fmt.Sprintf("%s:%d", smtpServer, smtpPort)
	debugPrint("Connecting to SMTP server: %s (TLS)", smtpAddr)

	tlsConfig := &tls.Config{
		ServerName:         smtpServer,
		MinVersion:         tls.VersionTLS12,
		InsecureSkipVerify: false, // Do NOT skip SSL verification (debug if needed)
	}
	conn, err := tls.Dial("tcp", smtpAddr, tlsConfig)
	if err != nil {
		debugPrint("TLS Dial FAILED: %v", err)
		return C.CString(fmt.Sprintf("TLS fail: %v", err))
	}
	defer conn.Close()
	debugPrint("TLS Connected successfully")

	// --------------------------
	// Debug 6: SMTP Client Initialization
	// --------------------------
	client, err := smtp.NewClient(conn, smtpServer)
	if err != nil {
		debugPrint("SMTP Client FAILED: %v", err)
		return C.CString(fmt.Sprintf("SMTP client fail: %v", err))
	}
	defer client.Quit()
	debugPrint("SMTP Client created")

	// --------------------------
	// Debug 7: SMTP Authentication (common failure point)
	// --------------------------
	debugPrint("Authenticating with SMTP server: sender=%s, server=%s", senderEmail, smtpServer)
	auth := smtp.PlainAuth("", senderEmail, senderKey, smtpServer)
	if err := client.Auth(auth); err != nil {
		debugPrint("SMTP Auth FAILED: %v", err)
		// Common auth errors: wrong key, 2FA enabled, server blocked IP
		return C.CString(fmt.Sprintf("auth fail: %v (check QQ app key/IP)", err))
	}
	debugPrint("SMTP Authenticated successfully")

	// --------------------------
	// Debug 8: Set Sender (must match authenticated email)
	// --------------------------
	debugPrint("Setting sender: %s", senderEmail)
	if err := client.Mail(senderEmail); err != nil {
		debugPrint("Set Sender FAILED: %v", err)
		return C.CString(fmt.Sprintf("set sender fail: %v", err))
	}

	// --------------------------
	// Debug 9: Add Recipients (track individual failures)
	// --------------------------
	for _, recp := range recipients {
		debugPrint("Adding recipient to SMTP: %s", recp)
		if err := client.Rcpt(recp); err != nil {
			debugPrint("Add Recipient %s FAILED: %v", recp, err)
			return C.CString(fmt.Sprintf("add recipient %s fail: %v", recp, err))
		}
	}
	debugPrint("All recipients added to SMTP")

	// --------------------------
	// Debug 10: Send Email Body
	// --------------------------
	debugPrint("Sending email body (length: %d bytes)", len(emailContent))
	dataWriter, err := client.Data()
	if err != nil {
		debugPrint("Open Data Stream FAILED: %v", err)
		return C.CString(fmt.Sprintf("open data stream fail: %v", err))
	}
	defer dataWriter.Close()

	bytesWritten, err := dataWriter.Write([]byte(emailContent))
	if err != nil {
		debugPrint("Write Body FAILED: %v", err)
		return C.CString(fmt.Sprintf("write body fail: %v", err))
	}
	debugPrint("Wrote %d bytes to SMTP server", bytesWritten)

	// --------------------------
	// Debug 11: Final Check (server may still reject after write)
	// --------------------------
	if err := dataWriter.Close(); err != nil {
		debugPrint("Close Data Stream FAILED: %v", err)
		return C.CString(fmt.Sprintf("close writer fail: %v", err))
	}
	debugPrint("=== Email send process completed (no errors reported) ===")

	return nil
}

//export SendEmailCleanup
func SendEmailCleanup(state *C.EmailState) {
	if state == nil {
		debugPrint("Cleanup: nil state")
		return
	}
	debugPrint("Starting EmailState cleanup")

	// Free recipients
	recipientCount := int(state.recipient_count)
	for i := 0; i < recipientCount; i++ {
		cRecipient := C.getCharArrayElement(state.recipients, C.int(i))
		C.free(unsafe.Pointer(cRecipient))
	}
	C.free(unsafe.Pointer(state.recipients))

	// Free attachments
	attachmentCount := int(state.attachment_count)
	for i := 0; i < attachmentCount; i++ {
		cAttachment := C.getCharArrayElement(state.attachments, C.int(i))
		C.free(unsafe.Pointer(cAttachment))
	}
	C.free(unsafe.Pointer(state.attachments))

	// Free other fields
	if state.subject != nil {
		C.free(unsafe.Pointer(state.subject))
	}
	if state.message_text != nil {
		C.free(unsafe.Pointer(state.message_text))
	}
	if state.message_file != nil {
		C.free(unsafe.Pointer(state.message_file))
	}
	if state.error != nil {
		C.free(unsafe.Pointer(state.error))
	}

	// Free SMTP fields
	if state.sender_email != nil {
		C.free(unsafe.Pointer(state.sender_email))
	}
	if state.sender_key != nil {
		C.free(unsafe.Pointer(state.sender_key))
	}
	if state.smtp_server != nil {
		C.free(unsafe.Pointer(state.smtp_server))
	}

	C.free(unsafe.Pointer(state))
	debugPrint("Cleanup completed")
}

//export SendEmailIgnoreInterrupts
func SendEmailIgnoreInterrupts() {
	debugPrint("Interrupt protection toggled")
}

func main() {}
